package Panaderia;

import Tipos.Stock;
import Tipos.TipoMensaje;
import Tipos.UbicacionPanaderia;
import Tipos.Buffer;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.logging.Level;
import java.util.logging.Logger;
import Tipos.Mensaje;
import java.io.ObjectInputStream;

/**
 * Encargado de actuar como panaderia.
 */
public class Panaderia {
    //variable que indica la cantidad de panaderias
    //que participan de la venta de panes.
    static int CANTIDADPANADERIAS = 5;
  
/*
 *   Si la cantidad de panes que tengo no
 *  me alcanza para cumplir la cantidad que necesita el cliente
 *  realizo un pedido es decir un mensaje con PEDIDO_PANES y la
 *  cantidad de panes que me hacen falta a las demas panaderias.
 *  La primer panaderia en devolverme la cantidad de panes que 
 *  necesito es la que participa del comit, los demas mensajes se desechan.
 *  Si ninguna tiene la cantidad que necesito, dejo ir al cliente.
 * 
 */
    private static void venderPanesDeTodos(int compra, UbicacionPanaderia direccionSucursal, ObjectOutputStream outStreamCoordinador, MulticastSocket socketUDP, InetAddress grupo, int puertoUDP, Buffer peticionesMostrador, Stock stock) {
        //borrar todas las peticionesMostrador
        peticionesMostrador.vaciar();
        int stockActual = stock.getStock();
        int pedir = compra - stockActual;
        Mensaje mensajePedido = new Mensaje(TipoMensaje.PEDIDO_PANES, pedir, direccionSucursal, null, direccionSucursal);
        DatagramPacket paquete = null;
        try {
            //solicito la cantidad de panes que me hacen falta a las demas panaderias
            paquete = new DatagramPacket(mensajePedido.toString().getBytes(), mensajePedido.toString().length(), grupo, puertoUDP);
            socketUDP.send(paquete);            
            boolean continuar = false;
            // Si me llega el mensaje que estaba esperando con la cantidad de panes que
            // necesito . entonces me comunico con el coordinador para hacer el commit de 2 fases
            
            for (int i = 0; i < 1000000000; i++) {
                for (int j = 0; j < 1000000000; j++) {
                    
                }    
            }
            
            for(int i = 0; i < CANTIDADPANADERIAS; i++){
                //System.out.println("entre al while de vender oanes de todosssssssssssssssssssssss");                
                Mensaje mensajeRecibido = peticionesMostrador.get();
                //System.out.println("ACA ESTA EL PUTO MENSAJE RECIBIDO--->" + mensajeRecibido);
                if(mensajeRecibido != null && mensajeRecibido.getCantPanes() >= pedir && mensajeRecibido.getMensajePanaderia() == TipoMensaje.RESPUESTA_PEDIDO){
                    System.out.println("La panaderia " + mensajeRecibido.getEmisor().getNombre() + " tiene los panes necesarios, intentare hacer commit");
                    Mensaje mensajeCoordinador = new Mensaje(TipoMensaje.DO_COMMIT, pedir, direccionSucursal, mensajeRecibido.getEmisor(), direccionSucursal);
                    outStreamCoordinador.writeObject(mensajeCoordinador);
                    continuar = true;
                    break;
                }
            }
            //analizo los mensajes que me llegan del Coordinador
            while(continuar){
                Mensaje mensajeRecibido = peticionesMostrador.get();
                Mensaje mensajeEnviar = new Mensaje();
                if (mensajeRecibido != null){
                    System.out.println("VenderPanesDeTodos: " + mensajeRecibido.mostrarPantalla());
                    switch (mensajeRecibido.getMensajePanaderia()) {
                        case VOTE_REQUEST : 
                            boolean commit = stock.prestar(stockActual);
                            if (commit){
                                mensajeEnviar.setMensajePanaderia(TipoMensaje.VOTE_COMMIT);
                            }else{
                                mensajeEnviar.setMensajePanaderia(TipoMensaje.VOTE_ABORT);
                            }
                            mensajeEnviar.setIdCommit(mensajeRecibido.getIdCommit());
                            mensajeEnviar.setCantPanes(stockActual);
                            mensajeEnviar.setEmisor(direccionSucursal);
                            mensajeEnviar.setIniciador(mensajeRecibido.getIniciador());
                            mensajeEnviar.setParticipanteCommit(mensajeRecibido.getParticipanteCommit());
                            outStreamCoordinador.writeObject(mensajeEnviar);
                            System.out.println("VenderPanesDeTodos Enviado: " + mensajeEnviar.mostrarPantalla());
                            break;
                        case GLOBAL_COMMIT:
                            System.out.println("Vendi " + stockActual + " panes, mas " + pedir +" panes de la panaderia " + mensajeRecibido.getParticipanteCommit().getNombre());
                            stock.confirmarPrestamo();
                            continuar = false;
                            break;
                        case GLOBAL_ABORT:
                            System.out.println("No pude vender hice GLOBAL_ABORT");
                            stock.deshacerPrestamo();
                            continuar = false;
                            break;
                    }
                }
            }
            
        } catch (SocketException ex) {
            Logger.getLogger(Panaderia.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Panaderia.class.getName()).log(Level.SEVERE, null, ex);
        } 
            
    }
    
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        try {
            //Stock inicial de cada panaderia
            Stock stock= new Stock(150);
            //buffer de panaderos
            Buffer peticionesProcesador = new Buffer();
            Buffer peticionesMostrador = new Buffer();
            
            //conexion Multicast UDP
            InetAddress grupo = InetAddress.getByName("226.25.1.36");
            MulticastSocket socketUDP = null;    
            PanaderoEscuchadorUDP panaderoEscuchadorUDP = null;
            PanaderoProcesador panaderoProcesador = null;    
            //conexion con servidor TCP
            int puertoCoordinador = 4020 ;
            int puertoUDP = 6789;
            Socket socketTCPCoordinador = new Socket(InetAddress.getLocalHost(), puertoCoordinador);
            UbicacionPanaderia direccionSucursal = new UbicacionPanaderia();
            ObjectOutputStream outStreamCoordinador = new ObjectOutputStream(socketTCPCoordinador.getOutputStream());
            ObjectInputStream inStreamCoordinador = new ObjectInputStream(socketTCPCoordinador.getInputStream());
            PanaderoEscuchadorTCP panaderoEscuchadorTCP = new PanaderoEscuchadorTCP(inStreamCoordinador, direccionSucursal, peticionesMostrador, peticionesProcesador);
            panaderoEscuchadorTCP.start();
            //conexion Multicast UDP con las demas panaderias
            socketUDP = new MulticastSocket(puertoUDP);
            socketUDP.joinGroup(grupo);
            panaderoEscuchadorUDP = new PanaderoEscuchadorUDP(socketUDP,direccionSucursal, peticionesProcesador, peticionesMostrador);
            panaderoEscuchadorUDP.start(); 
            //Panadero procesador de peticiones
            panaderoProcesador = new PanaderoProcesador(peticionesProcesador, stock, direccionSucursal, outStreamCoordinador, socketUDP, grupo, puertoUDP);
            panaderoProcesador.start();
            boolean imprimirDireccion = true;
            //panadero Mostrador
            while(stock.atender()){
                if (imprimirDireccion){
                    System.out.println("Panaderia -------------> direccion sucursal: " + direccionSucursal.getNombre());
                    imprimirDireccion = false;
                }
                int compra = (int) Math.floor(Math.random()*6+1);
                //espera para que no venda todos los panes de una
                for (int i = 0; i < 100000; i++) {
                    for (int j = 0; j < 100000; j++) {
                    }
                }
                
                if(stock.vender(compra)){
                    System.out.println(" Vendi " + compra + " Panes");
                }else{
                    System.out.println("Tengo " + stock.getStock() + " panes, debo pedir prestado " + (compra - stock.getStock()) + " panes.");
                    venderPanesDeTodos(compra, direccionSucursal, outStreamCoordinador, socketUDP, grupo, puertoUDP, peticionesMostrador, stock);
                }
            }
            //CIERRE DE PANADERIA   
            
            Mensaje mensajeCierre = new Mensaje(TipoMensaje.PANADERIA_CERRADA,stock.getStock(),direccionSucursal, null, direccionSucursal);
            //ObjectOutputStream outStreamCoord = new ObjectOutputStream(socketTCPCoordinador.getOutputStream());
            outStreamCoordinador.writeObject(mensajeCierre);
            Mensaje mensajePedido = new Mensaje(TipoMensaje.PANADERIA_CERRADA, 0, direccionSucursal, null, direccionSucursal);
            DatagramPacket paquete = null;
            paquete = new DatagramPacket(mensajePedido.toString().getBytes(), mensajePedido.toString().length(), grupo, puertoUDP);
            socketUDP.send(paquete);  
            
            //cerramos los threads
//            panaderoEscuchadorTCP.cerrar(true);
//            panaderoEscuchadorUDP.cerrar(true);            
//            panaderoProcesador.cerrar(true);
//                //joins 
            try {
                System.out.println("Cerrando Threads");
                panaderoEscuchadorTCP.join();
                System.out.println("Cerro panaderoEscuchadorTCP");
                panaderoEscuchadorUDP.join();   
                System.out.println("Cerro panaderoEscuchadorUDP");
                panaderoProcesador.join();   
                System.out.println("Cerro panaderoProcesador");
                     
            } catch (InterruptedException ex) {
                Logger.getLogger(Panaderia.class.getName()).log(Level.SEVERE, null, ex);
            }
            
            System.out.println("Panaderia, mande a cerrar a todos ");
//            System.out.println("Cerro Procesador ");
//            //cierre del stream de salida
            outStreamCoordinador.close();
//            //cierre del socket TCP
            socketTCPCoordinador.close();   
//            //cierre del socket Multicast
            socketUDP.leaveGroup(grupo);
            socketUDP.close();
            System.out.println("Cierro la panaderia " + direccionSucursal.getNombre());
            System.exit(0); 
        } catch (UnknownHostException ex) {
            Logger.getLogger(Panaderia.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Panaderia.class.getName()).log(Level.SEVERE, null, ex);
        } 
    }
}
